package me.exchange.com.modules.system.service.impl;

import me.exchange.com.exception.BadRequestException;
import me.exchange.com.exception.EntityExistException;
import me.exchange.com.modules.system.domain.Menu;
import me.exchange.com.modules.system.domain.User;
import me.exchange.com.modules.system.domain.vo.MenuVo;
import me.exchange.com.modules.system.dto.MenuDto;
import me.exchange.com.modules.system.dto.MenuQueryCriteria;
import me.exchange.com.modules.system.dto.RoleSmallDto;
import me.exchange.com.modules.system.mapper.MenuMapper;
import me.exchange.com.modules.system.repository.MenuRepository;
import me.exchange.com.modules.system.repository.UserRepository;
import me.exchange.com.modules.system.service.MenuService;
import me.exchange.com.modules.system.service.RoleService;
import me.exchange.com.utils.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheConfig;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.sql.Timestamp;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author Zheng Jie
 */
@Service
@CacheConfig(cacheNames = "menu")
@Transactional(propagation = Propagation.SUPPORTS, readOnly = true, rollbackFor = Exception.class)
public class MenuServiceImpl implements MenuService {

    @Autowired
    private MenuRepository menuRepository;
    @Autowired
    private MenuMapper menuMapper;
    @Autowired
    private RoleService roleService;
    @Autowired
    private UserRepository userRepository;

    @Override
    @Cacheable
    public List<MenuDto> queryAll(MenuQueryCriteria criteria){
//        Sort sort = new Sort(Sort.Direction.DESC,"id");
        Sort sort = criteria.getSort();
        return menuMapper.toDto(menuRepository.findAll((root, criteriaQuery, criteriaBuilder) -> QueryHelp.getPredicate(root,criteria,criteriaBuilder),sort));
    }

    @Override
    @Cacheable
    public Object queryAll(MenuQueryCriteria criteria, Pageable pageable){
//        Sort sort = new Sort(Sort.Direction.DESC,"id");
        return PageUtil.toPage(menuRepository.findAll((root, criteriaQuery, criteriaBuilder) -> QueryHelp.getPredicate(root,criteria,criteriaBuilder),pageable));
    }

    @Override
    @Cacheable(key = "#p0")
    public MenuDto findById(long id) {
        Menu menu = menuRepository.findById(id).orElseGet(Menu::new);
        ValidationUtil.isNull(menu.getId(),"Menu","id",id);
        return menuMapper.toDto(menu);
    }

    @Override
    public List<MenuDto> findByRoles(List<RoleSmallDto> roles) {
        Set<Long> roleIds = roles.stream().map(RoleSmallDto::getId).collect(Collectors.toSet());
        LinkedHashSet<Menu> menus = menuRepository.findByRoles_IdInAndTypeNotOrderBySortAsc(roleIds, 3);
        return menus.stream().map(menuMapper::toDto).collect(Collectors.toList());
    }

    @Override
    @CacheEvict(allEntries = true)
    public MenuDto create(Menu resources) {
        if(menuRepository.findByResourcesName(resources.getResourcesName()) != null){
            throw new EntityExistException(Menu.class,"resources_name",resources.getResourcesName());
        }
        User user=userRepository.findByUsername(SecurityUtils.getUsername());
        resources.setCAdminId(user.getId());
        resources.setCAdminTime(new Timestamp(System.currentTimeMillis()));
        resources.setIsdel(true);
        return menuMapper.toDto(menuRepository.save(resources));
    }

    @Override
    @CacheEvict(allEntries = true)
    public void update(Menu resources) {
        if(resources.getId().equals(resources.getParentId())) {
            throw new BadRequestException("上级不能为自己");
        }
        Menu menu = menuRepository.findById(resources.getId()).orElseGet(Menu::new);
        ValidationUtil.isNull(menu.getId(),"Permission","id",resources.getId());

        Menu menu1 = menuRepository.findByResourcesName(resources.getResourcesName());
        User user=userRepository.findByUsername(SecurityUtils.getUsername());
        if(menu1 != null && !menu1.getId().equals(menu.getId())){
            throw new EntityExistException(Menu.class,"name",resources.getResourcesName());
        }
        resources =(Menu)BeanUtil.merge(resources,menu);
        resources.setUAdminId(user.getId());
        resources.setUAdminTime(new Timestamp(System.currentTimeMillis()));
        menuRepository.save(resources);
    }

    @Override
    @CacheEvict(allEntries = true)
    @Transactional(rollbackFor = Exception.class)
    public void updateStatus(MenuQueryCriteria criteria) {
        User user=userRepository.findByUsername(SecurityUtils.getUsername());
        Menu menu = menuRepository.findById(criteria.getId()).orElseGet(Menu::new);
        ValidationUtil.isNull( menu.getId(),"menu","id",criteria.getId());
        menu.setUAdminId(user.getId());
        menu.setUAdminTime(new Timestamp(System.currentTimeMillis()));
        menu.setId(menu.getId());
        menu.setStatus(criteria.getStatus());
        menuRepository.save(menu);
    }
    @Override
    public Set<Menu> getDeleteMenus(List<Menu> menuList, Set<Menu> menuSet) {
        // 递归找出待删除的菜单
        for (Menu menu1 : menuList) {
            menuSet.add(menu1);
            List<Menu> menus = menuRepository.findByParentId(menu1.getId());
            if(menus!=null && menus.size()!=0){
                getDeleteMenus(menus, menuSet);
            }
        }
        return menuSet;
    }

    @Override
    @CacheEvict(allEntries = true)
    @Transactional(rollbackFor = Exception.class)
    public void delete(Set<Menu> menuSet) {
        Menu del_menu = null;
        for (Menu menu : menuSet) {
            roleService.untiedMenu(menu.getId());
            del_menu = menuRepository.findById(menu.getId()).orElseGet(Menu::new);
            del_menu.setIsdel(false);
            menuRepository.save(del_menu);
            //menuRepository.deleteById(menu.getId());
        }
    }

    @Override
    @Cacheable(key = "'tree'")
    public Object getMenuTree(List<Menu> menus) {
        List<Map<String,Object>> list = new LinkedList<>();
        menus.forEach(menu -> {
                    if (menu!=null){
                        List<Menu> menuList = menuRepository.findByParentId(menu.getId());
                        Map<String,Object> map = new HashMap<>(16);
                        map.put("id",menu.getId());
                        map.put("label",menu.getResourcesName());
                        map.put("icon",menu.getIcon());
                        map.put("path",menu.getPath());
                        map.put("permission",menu.getPermission());
                        if(menuList!=null && menuList.size()!=0){
                            map.put("children",getMenuTree(menuList));
                        }
                        list.add(map);
                    }
                }
        );
        return list;
    }

    @Override
    @Cacheable(key = "'pid:'+#p0")
    public List<Menu> findByPid(long pid) {
        return menuRepository.findByParentId(pid);
    }

    @Override
    public Map<String,Object> buildTree(List<MenuDto> menuDtos) {
        List<MenuDto> trees = new ArrayList<>();
        Set<Long> ids = new HashSet<>();
        for (MenuDto menuDTO : menuDtos) {
            if (menuDTO.getParentId() == 0) {
                trees.add(menuDTO);
            }
            for (MenuDto it : menuDtos) {
                if (it.getParentId().equals(menuDTO.getId())) {
                    if (menuDTO.getChildren() == null) {
                        menuDTO.setChildren(new ArrayList<>());
                    }
                    menuDTO.getChildren().add(it);
                    ids.add(it.getId());
                }
            }
        }
        Map<String,Object> map = new HashMap<>(2);
        if(trees.size() == 0){
            trees = menuDtos.stream().filter(s -> !ids.contains(s.getId())).collect(Collectors.toList());
        }
        map.put("content",trees);
        map.put("totalElements", menuDtos.size());
        return map;
    }

    @Override
    public List<MenuVo> buildMenus(List<MenuDto> menuDtos) {
        List<MenuVo> list = new LinkedList<>();

        menuDtos.forEach(menuDTO -> {
            if (menuDTO!=null){
                List<MenuDto> menuDtoList = menuDTO.getChildren();
                MenuVo menuVo = new MenuVo();
                // 一级目录需要加斜杠，不然会报警告
                menuVo.setPath("");
                if(menuDtoList !=null && menuDtoList.size()!=0){
                    menuVo.setAlwaysShow(true);
                    menuVo.setRedirect("noredirect");
                    menuVo.setResourcesName(menuDTO.getResourcesName());
                    menuVo.setResourcesCode(menuDTO.getResourcesCode());
                    menuVo.setIcon(menuDTO.getIcon());
                    menuVo.setChildren(buildMenus(menuDtoList));
                    menuVo.setId(menuDTO.getId());
                    // 处理是一级菜单并且没有子菜单的情况
                } else if(menuDTO.getType() == 2){
                    menuVo.setResourcesName(menuDTO.getResourcesName());
                    menuVo.setResourcesCode(menuDTO.getResourcesCode());
                    menuVo.setIcon(menuDTO.getIcon());
                    menuVo.setPath(menuDTO.getPath());
                    menuVo.setId(menuDTO.getId());
                }
                list.add(menuVo);
            }
        }
        );
        return list;
    }

    @Override
    public Menu findOne(Long id) {
        Menu menu = menuRepository.findById(id).orElseGet(Menu::new);
        ValidationUtil.isNull(menu.getId(),"Menu","id",id);
        return menu;
    }

    @Override
    public void download(List<MenuDto> menuDtos, HttpServletResponse response) throws IOException {
        List<Map<String, Object>> list = new ArrayList<>();
        for (MenuDto menuDTO : menuDtos) {
            Map<String,Object> map = new LinkedHashMap<>();
            map.put("菜单名称", menuDTO.getResourcesName());
            map.put("菜单类型", menuDTO.getType() == 0 ? "目录" : menuDTO.getType() == 1 ? "菜单" : "按钮");
            map.put("权限标识", menuDTO.getPermission());
            map.put("创建日期", menuDTO.getCAdminTime());
            list.add(map);
        }
        FileUtil.downloadExcel(list, response);
    }
}
